package com.xxl.sso.server.controller;

import com.xxl.sso.core.conf.Conf;
import com.xxl.sso.core.login.SsoWebLoginHelper;
import com.xxl.sso.core.store.SsoLoginStore;
import com.xxl.sso.core.user.XxlSsoUser;
import com.xxl.sso.core.store.SsoSessionIdHelper;
import com.xxl.sso.server.core.model.UserInfo;
import com.xxl.sso.server.core.result.ReturnT;
import com.xxl.sso.server.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

/**
 * sso server (for web)
 *
 * @author xuxueli 2017-08-01 21:39:47
 */
@Controller
public class WebController {

    @Autowired
    private UserService userService;

    @RequestMapping("/")
    public String index(Model model, HttpServletRequest request, HttpServletResponse response) {

        // login check
        XxlSsoUser xxlUser = SsoWebLoginHelper.loginCheck(request, response);

        if (xxlUser == null) {
            return "redirect:/login";
        } else {
            model.addAttribute("xxlUser", xxlUser);// 将model添加的attribute添加到return返回的html页面中。
            return "index";
        }
    }

    /**
     * Login page
     *
     * @param model
     * @param request
     * @return
     */
    @RequestMapping(Conf.SSO_LOGIN)
    public String login(Model model, HttpServletRequest request, HttpServletResponse response) {

        //判断之前其他服务有没有登录过sso-server，如果有 就能拿到名为xxl_sso_sessionid的sso-server的cookie，如果根据cookie从redis中获取的xxlUser!=null，证明已经在其他服务登录过了 不需要再登录了 直接返回客户端请求的页面，并传参sso-server的cookie的值给该次请求的服务 让它创建它的cookie 这样之后对它的请求就可以根据它的cookie查询出redis中的用户数据 就证明登录了，创建该次请求的服务的cookie是以便该次请求的服务保存它的登录态 就不用每次都请求sso-server进行鉴权了；如果xxlUser=null 就需要进行登录
        //根据浏览器requestHeader中的sso-server的cookie判断之前其他服务有没有登录过sso-server，如果登录过，则该次请求的服务不需要再次登录 即单点登录的意思
        // login check
        XxlSsoUser xxlUser = SsoWebLoginHelper.loginCheck(request, response);

        if (xxlUser != null) {//TODO 什么情况下xxlUser!=null？？？既然是能走到这里就说明之前没有登录过，难道是用户登录后再次请求该接口？不可能啊，客户端就控制了。估计是开发者的习惯吧？ 此情况是在又有个服务接受请求时 发现浏览器上没有服务的cookie 就请求sso-server 因为上次已经有其他服务登录过sso-server 浏览器上有了sso-server的cookie 就会携带上sso-server的cookie，这种情况下xxlUser!=null，于是直接重定向到客户端请求的页面，然后在浏览器上创建这次服务的cookie，便于不每次登录都请求sso-server进行鉴权。

            // success redirect
            String redirectUrl = request.getParameter(Conf.REDIRECT_URL);
            if (redirectUrl!=null && redirectUrl.trim().length()>0) {

                String sessionId = SsoWebLoginHelper.getSessionIdByCookie(request);// 供该次请求的服务创建它的cookie
                String redirectUrlFinal = redirectUrl + "?" + Conf.SSO_SESSIONID + "=" + sessionId;

                return "redirect:" + redirectUrlFinal;
            } else {
                return "redirect:/";// redirect: 既可以重定向到controller层的接口上也可以重定向到http请求
            }
        }

        //xxlUser=null 即没有登录过，返回login页面
        model.addAttribute("errorMsg", request.getParameter("errorMsg"));// 将model添加的attribute添加到return返回的html页面中。
        model.addAttribute(Conf.REDIRECT_URL, request.getParameter(Conf.REDIRECT_URL));// redirect_url
        return "login";
    }

    /**
     * Login
     *
     * @param request
     * @param redirectAttributes
     * @param username
     * @param password
     * @return
     */
    @RequestMapping("/doLogin")
    public String doLogin(HttpServletRequest request,
                        HttpServletResponse response,
                        RedirectAttributes redirectAttributes,
                        String username,
                        String password,
                        String ifRemember) {

        boolean ifRem = (ifRemember!=null&&"on".equals(ifRemember))?true:false;

        // valid login
        ReturnT<UserInfo> result = userService.findUser(username, password);

        //如果查询不到用户数据 重新登录
        if (result.getCode() != ReturnT.SUCCESS_CODE) {
            redirectAttributes.addAttribute("errorMsg", result.getMsg());

            redirectAttributes.addAttribute(Conf.REDIRECT_URL, request.getParameter(Conf.REDIRECT_URL));
            return "redirect:/login";
        }

        //如果查询到了用户数据 则redis缓存用户数据
        // 1、make xxl-sso user
        XxlSsoUser xxlUser = new XxlSsoUser();
        xxlUser.setUserid(String.valueOf(result.getData().getUserid()));
        xxlUser.setUsername(result.getData().getUsername());//TODO marvel中应该是phone
        xxlUser.setVersion(UUID.randomUUID().toString().replaceAll("-", ""));
        xxlUser.setExpireMinute(SsoLoginStore.getRedisExpireMinute());
        xxlUser.setExpireFreshTime(System.currentTimeMillis());


        // 2、make session id
        String sessionId = SsoSessionIdHelper.makeSessionId(xxlUser);

        //make登录态 即cookie，可根据cookie查询redis中的value（如果redis中存在value 且 token的version=value的version 则已登录）
        // 3、login, store storeKey + cookie sessionId
        SsoWebLoginHelper.login(response, sessionId, xxlUser, ifRem);// redisKey=xxl_sso_sessionid#userId

        // 4、return, redirect sessionId
        String redirectUrl = request.getParameter(Conf.REDIRECT_URL);//TODO 通过getParameter()取值 和 参数中的username、password有什么区别？？？
        if (redirectUrl!=null && redirectUrl.trim().length()>0) {
            //TODO 参数中为什么会有这个xxl_sso_sessionid？？？为了在该次请求的服务中创建它的cookie 这样之后对它的请求就可以根据它的cookie查询出redis中的用户数据 就证明登录了，创建该次请求的服务的cookie是以便该次请求的服务保存它的登录态 就不用每次都请求sso-server进行鉴权了
            String redirectUrlFinal = redirectUrl + "?" + Conf.SSO_SESSIONID + "=" + sessionId;// http://xxlssoserver.com:8081/xxl-sso-web-sample-springboot/?xxl_sso_sessionid=sessionId
            return "redirect:" + redirectUrlFinal;//redirect: 和 response.sendRedirect 都是既可以直接写controller层的接口 也可以写http链接。
        } else {// 如果不是从其他服务器过来的 则返回本服务器的index.ftl页面
            return "redirect:/";
        }

    }

    /**
     * Logout
     *
     * @param request
     * @param redirectAttributes
     * @return
     */
    @RequestMapping(Conf.SSO_LOGOUT)
    public String logout(HttpServletRequest request, HttpServletResponse response, RedirectAttributes redirectAttributes) {

        //删除cookie + redis
        // logout
        SsoWebLoginHelper.logout(request, response);

        redirectAttributes.addAttribute(Conf.REDIRECT_URL, request.getParameter(Conf.REDIRECT_URL));// redirectAttributes.addAttribute() 可以在controller层之间进行传参，这里重定向到controller层的/login接口时，/login接口就可以通过request.getParameter()来获取到这里的redirectAttributes.addAttribute()的属性了。
        return "redirect:/login";
    }


}
